from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path
from typing import Any, Dict, Optional

import os

try:  # Python 3.11+
    import tomllib as _toml
except Exception:  # pragma: no cover
    _toml = None  # type: ignore


DEFAULT_CONFIG_LOCATIONS = [
    Path("/etc/cat_photo_ui/config.toml"),
    Path.home() / ".config/cat_photo_ui/config.toml",
    Path(__file__).resolve().parents[2] / "config.toml",  # project root if run from source
]


@dataclass
class AppConfig:
    photo_dir: Optional[str] = "/mnt/tfcard/nextcloud_share/手机相册"
    interval: Optional[int] = 8
    shuffle: Optional[bool] = True
    people_priority: Optional[bool] = False
    people_weight: Optional[float] = 2.0
    screen_name: Optional[str] = None
    screen_index: Optional[int] = None
    prefer_hdmi: Optional[bool] = True
    exclusive_hdmi: Optional[bool] = False
    # Fancy slideshow options
    fancy: Optional[bool] = True
    angle_range: Optional[int] = 15
    height_min: Optional[float] = 0.6
    height_max: Optional[float] = 1.0
    stack_size: Optional[int] = 4
    in_duration: Optional[int] = 1200
    out_duration: Optional[int] = 800

    @staticmethod
    def from_dict(data: Dict[str, Any]) -> "AppConfig":
        cfg = AppConfig()
        # top-level keys or nested under [slideshow] / [display]
        slideshow = data.get("slideshow", {})
        display = data.get("display", {})
        fancy = data.get("fancy", {})

        def pick(*keys, default=None):
            for k in keys:
                if k in data:
                    return data.get(k)
            return default

        cfg.photo_dir = pick("photo_dir", default=slideshow.get("photo_dir", cfg.photo_dir))
        cfg.interval = pick("interval", default=slideshow.get("interval", cfg.interval))
        cfg.shuffle = pick("shuffle", default=slideshow.get("shuffle", cfg.shuffle))
        cfg.people_priority = pick(
            "people_priority", default=slideshow.get("people_priority", cfg.people_priority)
        )
        cfg.people_weight = pick(
            "people_weight", default=slideshow.get("people_weight", cfg.people_weight)
        )

        cfg.screen_name = pick("screen_name", default=display.get("screen_name", cfg.screen_name))
        cfg.screen_index = pick("screen_index", default=display.get("screen_index", cfg.screen_index))
        cfg.prefer_hdmi = pick("prefer_hdmi", default=display.get("prefer_hdmi", cfg.prefer_hdmi))
        cfg.exclusive_hdmi = pick(
            "exclusive_hdmi", default=display.get("exclusive_hdmi", cfg.exclusive_hdmi)
        )
        # Fancy
        cfg.fancy = pick("fancy", default=fancy.get("enabled", cfg.fancy))
        cfg.angle_range = pick("angle_range", default=fancy.get("angle_range", cfg.angle_range))
        cfg.height_min = pick("height_min", default=fancy.get("height_min", cfg.height_min))
        cfg.height_max = pick("height_max", default=fancy.get("height_max", cfg.height_max))
        cfg.stack_size = pick("stack_size", default=fancy.get("stack_size", cfg.stack_size))
        cfg.in_duration = pick("in_duration", default=fancy.get("in_duration", cfg.in_duration))
        cfg.out_duration = pick("out_duration", default=fancy.get("out_duration", cfg.out_duration))
        return cfg


def load_toml_file(path: Path) -> Dict[str, Any]:
    if _toml is None:
        return {}
    try:
        if not path.exists():
            return {}
        with path.open("rb") as f:
            return _toml.load(f)  # type: ignore
    except Exception:
        return {}


def discover_and_load_config(explicit_path: Optional[str]) -> AppConfig:
    if explicit_path:
        data = load_toml_file(Path(explicit_path))
        return AppConfig.from_dict(data)
    for p in DEFAULT_CONFIG_LOCATIONS:
        data = load_toml_file(p)
        if data:
            return AppConfig.from_dict(data)
    return AppConfig()  # defaults


def merge_args_with_config(args, cfg: AppConfig):
    # 仅当参数仍为 argparse 的默认值时，才用配置值覆盖
    # 构造默认映射用于比较
    defaults = {
        "photo_dir": "/mnt/tfcard/nextcloud_share/手机相册",
        "interval": 8,
        "shuffle": True,
        "people_priority": False,
        "people_weight": 2.0,
        "screen_name": None,
        "screen_index": None,
        "prefer_hdmi": True,
        "exclusive_hdmi": False,
        "fancy": False,
        "angle_range": 15,
        "height_min": 0.6,
        "height_max": 1.0,
        "stack_size": 4,
        "in_duration": 1200,
        "out_duration": 800,
    }

    def maybe_set(attr: str, value):
        if getattr(args, attr, None) == defaults.get(attr) and value is not None:
            setattr(args, attr, value)

    maybe_set("photo_dir", cfg.photo_dir)
    maybe_set("interval", cfg.interval)
    maybe_set("shuffle", cfg.shuffle)
    maybe_set("people_priority", cfg.people_priority)
    maybe_set("people_weight", cfg.people_weight)
    maybe_set("screen_name", cfg.screen_name)
    maybe_set("screen_index", cfg.screen_index)
    maybe_set("prefer_hdmi", cfg.prefer_hdmi)
    maybe_set("exclusive_hdmi", cfg.exclusive_hdmi)
    maybe_set("fancy", cfg.fancy)
    maybe_set("angle_range", cfg.angle_range)
    maybe_set("height_min", cfg.height_min)
    maybe_set("height_max", cfg.height_max)
    maybe_set("stack_size", cfg.stack_size)
    maybe_set("in_duration", cfg.in_duration)
    maybe_set("out_duration", cfg.out_duration)
    return args


